﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Input.Touch;
using Services;
//using XNAColor = Microsoft.Xna.Framework.Color;
using Scopa.Menu;
using Scopa.GameSound;

namespace Scopa.GamePlay
{
  public struct PlayerArea
  {
    public Vector2 blockPosition;
    public int width;
    public int height;
    public Vector2 namePos;
    public Vector2[] slotCoords;
    public float cardsRotation;
    public Vector2[] takeCoords;
  }



  /// <summary>
  /// Disegna lo stato offerto dal servizio IBoardState e raccoglie e segnala i click
  /// sulle entità disegnate
  /// </summary>
  public abstract class BoardRenderer : BaseRenderer
  {
    protected static readonly int CARD_WIDTH = 90;
    protected static readonly int CARD_HEIGHT = 154;
    static public readonly float TABLE_CARD_DEPTH = 0.2f;
    static public readonly float FLOATING_CARD_DEPTH = 0.4f;
    static public readonly float HAND_CARD_DEPTH = 0.3f;
    static readonly int CARDS_SPACING = 10;
    private static readonly int H_BLOCK_WIDTH = 600;
    private static readonly int H_BLOCK_HEIGHT = 100;

    // Compatibilità
    //public bool Hints = true, Outro = false;

    private Rectangle packPosition;
    private Card draggedCard;
    private Vector2 startingCardPosition;
    protected PlayerArea[] playerAreas;


    private Texture2D background;


    public BoardRenderer(Game game)
      : base(game)
    {
      UpdateOrder = (int)ComponentOrders.RENDERER;
      DrawOrder = (int)MenuDepths.GameLayer;
    }


    public override void Initialize()
    {
      gameState = Game.Services.GetService(typeof(BoardState)) as BoardState;
      gameBounds = Game.GraphicsDevice.Viewport.Bounds;

      // Setup cards
      draggedCard = null;
      startingCardPosition = new Vector2();

      // Pack position
      //packPosition = new Rectangle(gameBounds.Width - 15, gameBounds.Top + 10, CARD_WIDTH, CARD_HEIGHT);

      // Load Game
      //renderState = RenderState.Intro;

      // If the current game is a loaded game, don't play intro
      //if (Game.GetContext().currentGame != null)
      //{
      //  renderState = RenderState.Input;
      //  UpdateCards();
      //}

      base.Initialize();
    }

    protected override void LoadContent()
    {
      base.LoadContent();

      var content = Game.Content;

      texture_back_card = content.Load<Texture2D>(@"Images//Decks//Back");// + Game.GetContext().currentConfig.chosenDeck);
      background = content.Load<Texture2D>(@"Images//Backgrounds//Bg_" + Game.GetScopaContext().currentConfig.chosenBackground);
      Deck_Texture = Game.Content.Load<Texture2D>(@"Images//Decks//Deck_" + Game.GetScopaContext().currentConfig.chosenDeck);
      cardShadow = Game.Content.Load<Texture2D>(@"Images//Game//roundedBox");
      screen_transform = Game.GetScopaContext().ScreenTransform;

      // Initialize player areas
      playerAreas = new PlayerArea[gameState.Players.Count()];

      gameState.Players.ElementAt(0).Position = new Vector2(gameBounds.Center.X, gameBounds.Height + 200);
      if (gameState.Players.Count() == 2)
        gameState.Players.ElementAt(1).Position = new Vector2(gameBounds.Center.X, -200);
      else
        gameState.Players.ElementAt(1).Position = new Vector2(gameBounds.Right + 200, gameBounds.Center.Y);
      if (gameState.Players.Count() > 2)
        gameState.Players.ElementAt(2).Position = new Vector2(gameBounds.Center.X, -200);
      if (gameState.Players.Count() > 3)
        gameState.Players.ElementAt(3).Position = new Vector2(-200, gameBounds.Center.Y);


      // Player1 area
      playerAreas[0] = new PlayerArea()
      {
        blockPosition = new Vector2(gameBounds.Center.X, gameBounds.Bottom),
        width = H_BLOCK_WIDTH,
        height = H_BLOCK_HEIGHT,
        namePos = new Vector2(gameBounds.Right - 100 - BaveuseFont.MeasureString(gameState.Players.ElementAt(0).Name).X / 2, gameBounds.Bottom - 20),
        slotCoords = new Vector2[3],
        cardsRotation = 0.0f
      };
      var startY = playerAreas[0].blockPosition.Y;
      if (gameState.Players.Count(p => p.AIType == AIType.Human) == 1)
        startY -= 50;
      playerAreas[0].slotCoords[0] = new Vector2(playerAreas[0].blockPosition.X - (1.5f * CARD_WIDTH) - CARDS_SPACING, startY);
      playerAreas[0].slotCoords[1] = new Vector2(playerAreas[0].slotCoords[0].X + CARD_WIDTH + CARDS_SPACING, playerAreas[0].slotCoords[0].Y);
      playerAreas[0].slotCoords[2] = new Vector2(playerAreas[0].slotCoords[1].X + CARD_WIDTH + CARDS_SPACING, playerAreas[0].slotCoords[0].Y);


      // Player2 area
      playerAreas[1] = new PlayerArea()
      {
        blockPosition = new Vector2(gameBounds.Center.X, gameBounds.Top),
        width = H_BLOCK_WIDTH,
        height = H_BLOCK_HEIGHT,
        namePos = new Vector2(gameBounds.Right - 100 - BaveuseFont.MeasureString(gameState.Players.ElementAt(1).Name).X / 2, gameBounds.Top + 20),
        slotCoords = new Vector2[3],
        cardsRotation = MathHelper.ToRadians(180.0f)
      };
      playerAreas[1].slotCoords[0] = new Vector2(playerAreas[1].blockPosition.X - (1.5f * CARD_WIDTH) - CARDS_SPACING, playerAreas[1].blockPosition.Y - 20);
      playerAreas[1].slotCoords[1] = new Vector2(playerAreas[1].slotCoords[0].X + CARD_WIDTH + CARDS_SPACING, playerAreas[1].slotCoords[0].Y);
      playerAreas[1].slotCoords[2] = new Vector2(playerAreas[1].slotCoords[1].X + CARD_WIDTH + CARDS_SPACING, playerAreas[1].slotCoords[0].Y);

      if (gameState.Players.Count > 2)
      {
        playerAreas[1] = new PlayerArea()
        {
          blockPosition = new Vector2(gameBounds.Right, gameBounds.Center.Y + 30),
          width = H_BLOCK_WIDTH - 200,
          height = H_BLOCK_HEIGHT,
          namePos = new Vector2(gameBounds.Right - 20, gameBounds.Bottom - 10 - BaveuseFont.MeasureString(gameState.Players.ElementAt(1).Name).X / 2),
          slotCoords = new Vector2[3],
          cardsRotation = MathHelper.ToRadians(270.0f)
        };
        playerAreas[1].slotCoords[0] = new Vector2(playerAreas[1].blockPosition.X, playerAreas[1].blockPosition.Y - (1.5f * CARD_WIDTH) - CARDS_SPACING);
        playerAreas[1].slotCoords[1] = new Vector2(playerAreas[1].slotCoords[0].X, playerAreas[1].slotCoords[0].Y + CARD_WIDTH + CARDS_SPACING);
        playerAreas[1].slotCoords[2] = new Vector2(playerAreas[1].slotCoords[0].X, playerAreas[1].slotCoords[1].Y + CARD_WIDTH + CARDS_SPACING);

        // Player3 area
        playerAreas[2] = new PlayerArea()
        {
          blockPosition = new Vector2(gameBounds.Center.X, gameBounds.Top),
          width = H_BLOCK_WIDTH,
          height = H_BLOCK_HEIGHT,
          namePos = new Vector2(gameBounds.Right - 100 - BaveuseFont.MeasureString(gameState.Players.ElementAt(2).Name).X / 2, gameBounds.Top + 20),
          slotCoords = new Vector2[3],
          cardsRotation = MathHelper.ToRadians(180.0f)
        };
        playerAreas[2].slotCoords[0] = new Vector2(playerAreas[2].blockPosition.X - (1.5f * CARD_WIDTH) - CARDS_SPACING, playerAreas[2].blockPosition.Y);
        playerAreas[2].slotCoords[1] = new Vector2(playerAreas[2].slotCoords[0].X + CARD_WIDTH + CARDS_SPACING, playerAreas[2].slotCoords[0].Y);
        playerAreas[2].slotCoords[2] = new Vector2(playerAreas[2].slotCoords[1].X + CARD_WIDTH + CARDS_SPACING, playerAreas[2].slotCoords[0].Y);
      }

      // Player4 area
      if (gameState.Players.Count > 3)
      {
        playerAreas[3] = new PlayerArea()
        {
          blockPosition = new Vector2(gameBounds.Left, gameBounds.Center.Y + 30),
          width = H_BLOCK_WIDTH - 200,
          height = H_BLOCK_HEIGHT,
          namePos = new Vector2(gameBounds.Left + 20, gameBounds.Bottom - 10 - BaveuseFont.MeasureString(gameState.Players.ElementAt(3).Name).X / 2),
          slotCoords = new Vector2[3],
          cardsRotation = MathHelper.ToRadians(90.0f)
        };
        playerAreas[3].slotCoords[0] = new Vector2(playerAreas[3].blockPosition.X, playerAreas[3].blockPosition.Y - (1.5f * CARD_WIDTH) - CARDS_SPACING);
        playerAreas[3].slotCoords[1] = new Vector2(playerAreas[3].slotCoords[0].X, playerAreas[3].slotCoords[0].Y + CARD_WIDTH + CARDS_SPACING);
        playerAreas[3].slotCoords[2] = new Vector2(playerAreas[3].slotCoords[0].X, playerAreas[3].slotCoords[1].Y + CARD_WIDTH + CARDS_SPACING);
      }

      // Initial card states
      for (int i = 0; i < gameState.Players.Count(); i++)
      {
        for (int j = 0; j < gameState.Players.ElementAt(i).Cards.Count; j++)
        {
          var card = gameState.Players.ElementAt(i).Cards[j];
          card.CoordPosition = playerAreas[i].slotCoords[j];
          card.ShowBack = true;
          card.Width = CARD_WIDTH;
          card.Height = CARD_HEIGHT;
          card.SourceRectangle = new Rectangle((Deck_Texture.Width / 10) * (card.Value - 1), (Deck_Texture.Height / 4) * ((int)card.Seed), Deck_Texture.Width / 10, Deck_Texture.Height / 4);
          card.Rotation = playerAreas[i].cardsRotation;
          card.Depth = HAND_CARD_DEPTH;
          if ((gameState.Players.Count(p => p.AIType == AIType.Human) == 1) && (i == 0))
          {
            card.ShowBack = false;
            renderState = RenderState.Input;
          }
        }
      }

      // Cards on the table
      for (int i = 0; i < gameState.BoardBlocks.ElementAt(0).Cards.Count; i++)
      {
        Card c = gameState.BoardBlocks.ElementAt(0).Cards[i];
        c.CoordPosition = new Vector2(150 + (i * 120), 200);
        c.Width = CARD_WIDTH;
        c.Height = CARD_HEIGHT;
        c.SourceRectangle = new Rectangle((Deck_Texture.Width / 10) * (c.Value - 1), (Deck_Texture.Height / 4) * ((int)c.Seed), Deck_Texture.Width / 10, Deck_Texture.Height / 4);
        c.Rotation = 0.0f;
        c.Depth = TABLE_CARD_DEPTH;
      }

      // Cards in the pack
      foreach (Card c in gameState.Pack.Cards)
      {
        c.CoordPosition = new Vector2(packPosition.X, packPosition.Y);
        c.Width = CARD_WIDTH;
        c.Height = CARD_HEIGHT;
        c.SourceRectangle = new Rectangle((Deck_Texture.Width / 10) * (c.Value - 1), (Deck_Texture.Height / 4) * ((int)c.Seed), Deck_Texture.Width / 10, Deck_Texture.Height / 4);
        c.Rotation = 0.0f;
        c.ShowBack = true;
        c.Depth = HAND_CARD_DEPTH;
      }

      if (gameState.BoardBlocks.ElementAt(0).Cards.Count > 1)
      SpaceCards();
    }


    /// 
    /// UPDATE
    ///
    public override void Update(GameTime gameTime)
    {
      base.Update(gameTime);
    }

    public override void UpdateMain(GameTime gameTime)
    {
      foreach (var g in cachedGestures)
      {
        switch (g.GestureType)
        {
          case GestureType.Tap:
            break;
          case GestureType.DoubleTap:
            break;
          case GestureType.FreeDrag:
            HandleCardDrag(g, gameTime);
            break;
          case GestureType.DragComplete:
            ReleaseCard(g);
            break;
        }
      }
    }


    /// <summary>
    /// Releases the dragged card after a DragComplete gesture is recognized
    /// </summary>
    /// <param name="g">The gesture sample data</param>
    private void ReleaseCard(GestureSample g)
    {
      if (draggedCard != null)
      {
        // A dragged card has been released
        Rectangle draggedCardRect = new Rectangle((int)draggedCard.CoordPosition.X - (draggedCard.Width / 2), (int)draggedCard.CoordPosition.Y - (draggedCard.Height / 2), CARD_WIDTH, CARD_HEIGHT);
        draggedCard.IsBeingDragged = false;
        draggedCard.Depth = TABLE_CARD_DEPTH + 0.05f;
        gameState.BoardBlocks.ElementAt(0).Cards.ForEach(c => c.Depth = TABLE_CARD_DEPTH);

        var plArea = playerAreas[gameState.CurrentPlayerIndex];
        Rectangle blockRect;
        if ((plArea.cardsRotation % MathHelper.ToRadians(180f)) == 0)
          blockRect = new Rectangle((int)plArea.blockPosition.X - (plArea.width / 2), (int)plArea.blockPosition.Y - (plArea.height / 2), plArea.width, plArea.height);
        else
          blockRect = new Rectangle((int)plArea.blockPosition.X - (plArea.height / 2), (int)plArea.blockPosition.Y - (plArea.width / 2), plArea.height, plArea.width);
        if (blockRect.Contains(draggedCard.CoordPosition.ToPoint(screen_transform)))
        {
          // Put the card back in the player's hand
          draggedCard.CoordPosition = startingCardPosition;
          startingCardPosition = Vector2.Zero;
          draggedCard = null;
          return;
        }

        if (gameState.Players.ElementAt(gameState.CurrentPlayerIndex).Cards.Contains(draggedCard))
        {
          // The dragged card was in the player's hand
          List<Card> selectedCards = new List<Card>();
          // Check if the card has been released over other cards on the table
          foreach (Card c in gameState.BoardBlocks.ElementAt(0).Cards)
          {
            Rectangle tableCardRect = new Rectangle((int)c.CoordPosition.X - (c.Width / 2), (int)c.CoordPosition.Y - (c.Height / 2), CARD_WIDTH, CARD_HEIGHT);
            if (tableCardRect.Intersects(draggedCardRect))
              selectedCards.Add(c);
          }
          ReleasePlayerCard(gameState.CurrentPlayerIndex, draggedCard, selectedCards);
        }

        // Check if the card was released outside the table bounds
        Rectangle tableBounds = new Rectangle(gameBounds.Left + 100, gameBounds.Top + 100, gameBounds.Width - 200, gameBounds.Height - 200);
        if (!tableBounds.Contains(new Point((int)draggedCard.CoordPosition.X, (int)draggedCard.CoordPosition.Y)))
          SpaceCards();

        draggedCard = null;
      }
    }

    private void HandleCardDrag(GestureSample g, GameTime gameTime)
    {
      // Dragging delle carte
      if (draggedCard != null)
      {
        // A card is already being dragged
        draggedCard.CoordPosition = g.Position;
      }
      else
      {
        // Check if the player selected a card that was in their hand
        foreach (Card c in gameState.Players.ElementAt(gameState.CurrentPlayerIndex).Cards)
        {
          Rectangle cardRect = new Rectangle((int)c.CoordPosition.X - (c.Width / 2), (int)c.CoordPosition.Y - (c.Height / 2), c.Width, c.Height);
          if (cardRect.Contains(g.Position.ToPoint(screen_transform)))
          {
            // Start dragging a card from the player's hand
            c.IsBeingDragged = true;
            c.Depth = FLOATING_CARD_DEPTH;
            startingCardPosition = c.CoordPosition;
            draggedCard = c;
            // Play a sound
            soundManager.PlaySound((GamePlaySounds)random.Next((int)GamePlaySounds.CardFlip1, (int)GamePlaySounds.CardFlip5 + 1));
            return;
          }
        }
        // Check if the player selected a card on the table
        foreach (Card c in gameState.BoardBlocks.ElementAt(0).Cards)
        {
          Rectangle cardRect = new Rectangle((int)c.CoordPosition.X - (c.Width / 2), (int)c.CoordPosition.Y - (c.Height / 2), CARD_WIDTH, CARD_HEIGHT);
          if (cardRect.Contains(g.Position.ToPoint(screen_transform)))
          {
            // Start dragging a card from the table
            c.IsBeingDragged = true;
            c.Depth = FLOATING_CARD_DEPTH;
            c.Rotation = playerAreas[gameState.CurrentPlayerIndex].cardsRotation;
            startingCardPosition = c.CoordPosition;
            draggedCard = c;
            // Play a sound
            soundManager.PlaySound((GamePlaySounds)random.Next((int)GamePlaySounds.CardFlip1, (int)GamePlaySounds.CardFlip5 + 1));
            return;
          }
        }
      }
    }


    public override void UpdatePlayerBlocks(GameTime gameTime)
    {

    }

    /// <summary>
    /// Things to do when the turn changes
    /// </summary>
    /// <param name="gameTime"></param>
    public override void ChangeTurn(GameTime gameTime)
    {
      // Check if it's necessary to hide cards
      if (/*(gameState.Players[gameState.CurrentPlayerIndex].AIType == AIType.Human) &&*/ (gameState.Players.Count(p => p.AIType == AIType.Human) > 1))
      {
        // Check if the cards of the other players are visible, and hides them
        for (int i = 0; i < gameState.Players.Count; i++)
        {
          if (i != gameState.CurrentPlayerIndex)
          {
            // Hides the cards of the other players
            for (int j = 0; j < gameState.Players[i].Cards.Count; j++)
            {
              var card = gameState.Players[i].Cards[j];
              if (!card.ShowBack)
              {
                gameState.Animations.Add(new CardAnimation() { Card = card, AnimationType = AnimationType.ShowPlayerCards, AnimationFinished = false, Amount = -50, PlayerIndex = i });
                gameState.MossaFatta = true;
              }
            }
          }
        }
        if (gameState.Players[gameState.CurrentPlayerIndex].AIType == AIType.Human)
          renderState = RenderState.TurnStart;
      }

      // Space cards on the table only if the match is not finished
      if (gameState.Pack.Cards.Count == 0 && gameState.Players.All(p => p.Cards.Count() == 0))
        return;

      if (gameState.BoardBlocks.ElementAt(0).Cards.Count > 1)
        SpaceCards();
    }

    private void SpaceCards()
    {
      // Riordina le carte sul tavolo
      var XPosList = gameState.BoardBlocks.ElementAt(0).Cards.OrderBy(c => c.CoordPosition.X);
      var TableWidth = gameBounds.Width - 300;
      var TableHeight = gameBounds.Height - 300;
      var VDistance = TableWidth / gameState.BoardBlocks.ElementAt(0).Cards.Count;
      var HDistance = TableHeight / gameState.BoardBlocks.ElementAt(0).Cards.Count;      

      for (int i = 0; i < XPosList.Count(); i++)
      {
        float gapY = 0;
        if (TableHeight + 150 < XPosList.ElementAt(i).CoordPosition.Y)
          gapY = (TableHeight + 150) - XPosList.ElementAt(i).CoordPosition.Y;

        if (150 > XPosList.ElementAt(i).CoordPosition.Y)
          gapY = 150 - XPosList.ElementAt(i).CoordPosition.Y;

        var card = XPosList.ElementAt(i);
        card.Depth = TABLE_CARD_DEPTH;
        gameState.Animations.Add(new CardAnimation() { Card = card, AnimationFinished = false, Destination = new Vector2((VDistance * i) + 150 + (CARD_WIDTH / 2), XPosList.ElementAt(i).CoordPosition.Y + gapY), AnimationType = AnimationType.MoveTo });
      }

      if (gameState.Animations.Any())
        gameState.MossaFatta = true;

    }

    ///
    /// DRAW
    ///

    /// <summary>
    /// Disegna il tavolo di gioco
    /// </summary>
    public override void DrawBoard(GameTime gameTime)
    {
      spriteBatch.Draw(background, new Rectangle(0, 0, 800, 480), null, Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, 0.0f);
    }

    /// <summary>
    /// Disegna le carte in mano ai giocatori
    /// </summary>
    public override void DrawPlayerBlocks(GameTime gameTime)
    {
      for (int i = 0; i < gameState.Players.Count(); i++)
      {
        var player = gameState.Players.ElementAt(i);

        // Draw player rectangle and name
        var area = playerAreas[i];
        var blockRect = new Rectangle((int)area.blockPosition.X, (int)area.blockPosition.Y, area.width, area.height);

        // Highlight the current player block
        if (i == gameState.CurrentPlayerIndex)
          spriteBatch.Draw(box, blockRect, null, Color.Lerp(Color.CornflowerBlue, Color.Transparent, 0.1f), area.cardsRotation, new Vector2(box.Bounds.Center.X, box.Bounds.Center.Y), SpriteEffects.None, 0.1f);
        else
          spriteBatch.Draw(box, blockRect, null, Color.Lerp(Color.DimGray, Color.Transparent, 0.2f), area.cardsRotation, new Vector2(box.Bounds.Center.X, box.Bounds.Center.Y), SpriteEffects.None, 0.1f);

        // Draw player name
        var nameLen = BaveuseFont.MeasureString(player.Name);
        if (renderState != RenderState.Recap)
        {
          DrawCenteredMsg(BaveuseFont, player.Name, area.namePos + Vector2.One, Color.Black, Vector2.One, area.cardsRotation, 0.75f);
          DrawCenteredMsg(BaveuseFont, player.Name, area.namePos, player.Color, Vector2.One, area.cardsRotation, 0.78f);
        }
        else
        {
          DrawCenteredMsg(BaveuseFont, player.Name, area.namePos + Vector2.One, Color.Black, Vector2.One, area.cardsRotation, 0.95f);
          DrawCenteredMsg(BaveuseFont, player.Name, area.namePos, player.Color, Vector2.One, area.cardsRotation, 0.98f);
        }
        // Draw player cards
        if (player.Cards.Any() == false)
          continue;

        for (int j = 0; j < player.Cards.Count; j++)
          DrawCard(player.Cards[j]);
      }
    }


    public override void DrawTableCards(GameTime gameTime)
    {
      // Draw cards on the table
      var tableCards = gameState.BoardBlocks.ElementAt(0).Cards;

      for (int i = 0; i < tableCards.Count; ++i)
        DrawCard(tableCards[i]);
    }

    /// <summary>
    /// EVENTI
    /// </summary>

    public override event ReleasePlayerCard ReleasePlayerCard;
  }


}
